extern crate lazy_static;
extern crate log;

use anyhow;

use k8s_openapi::api::core::v1::Secret;

use kube::{
    self,
    api::{Api, ListParams},
};

use tracing_subscriber;

extern crate hyper;
extern crate hyper_tls;
extern crate tower;

use std::io::Read;

use std::result::Result::Ok;

use structopt::StructOpt;

//k8s  client封装
mod k8s_client;
use easy_base64::decode;
use libflate::gzip::{Decoder, Encoder};
//json处理
use serde_json::Value;
//yaml处理
use serde_yaml;

//tar文件处理
extern crate tar;

use std::fs;
use std::fs::File;
use std::io::Write;
use tar::{Builder, Header};
//命令行解析
#[derive(StructOpt)]
struct Opt {
    /// The pattern to look for
    /// ns
    #[structopt(short = "n", long = "namespace")]
    namespace: Option<String>,
    /// 输出文件路径
    #[structopt(short = "p", long = "path")]
    savepath: Option<String>,
    //是否导出全部
    #[structopt(short = "A", long = "--all")]
    all: bool,

    release: Option<String>,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    std::env::set_var("RUST_LOG", "info,kube=debug");
    tracing_subscriber::fmt::init();

    //命令行解析
    let opt = Opt::from_args();
    /*
     //命令行解析
     println!(
        "namespace={},file={},release={}",
        &opt.namespace.as_ref().unwrap(),
        &opt.file.as_ref().unwrap(),
        &opt.release.as_ref().unwrap(),

    );*/

    //保存路径
    let savepath = opt.savepath.as_ref().unwrap().clone();
    //是否导出全部
    let all = opt.all;
    //k8s客户端
    let client = k8s_client::init_k8s_client().await?;

    if (!all) {
        //ns
        let namespace = opt.namespace.as_ref().unwrap().clone();
        //已安装chart名
        let release = opt.release.as_ref().unwrap().clone();
        let secrets: Api<Secret> = Api::namespaced(client, &namespace);

        let lp = ListParams::default()
            .labels(&format!("name={},owner=helm,status=deployed", &release))
            .timeout(10);
        for helm_secret in secrets.list(&lp).await? {
            let secret_name = helm_secret.metadata.name.unwrap();
            println!("{}", &secret_name);
            if secret_name.starts_with("sh.helm.release.") {
                //获取secret里被base64加密的数据
                let release_data_base64 =
                    helm_secret.data.unwrap().get("release").unwrap().0.clone();
                //base64解密
                let release_data = decode(&release_data_base64);
                //gunzip 解密 gzip格式
                let mut decoder = Decoder::new(&release_data[..]).unwrap();
                let mut decoded_data = Vec::new();
                decoder.read_to_end(&mut decoded_data).unwrap();
                //gunzip解压后的字符串内容
                let release_str = String::from_utf8(decoded_data).unwrap();
                //println!("{}",&releaseStr)
                //分解处理解压后的release内容
                deal_with_release_str(
                    release_str.clone(),
                    release.clone(),
                    namespace.clone(),
                    savepath.clone(),
                );

                break;
            }
        }
    } else {
        let secrets: Api<Secret> = Api::all(client);

        let lp = ListParams::default()
            .labels("owner=helm,status=deployed")
            .timeout(10);
        for helm_secret in secrets.list(&lp).await? {
            let secret_name = helm_secret.metadata.name.unwrap();
            let secret_namespace = helm_secret.metadata.namespace.unwrap();
            if secret_name.starts_with("sh.helm.release.") {
                let secret_release_name = secret_name.split(".").collect::<Vec<_>>()[4].to_string();
                //获取secret里被base64加密的数据
                let release_data_base64 =
                    helm_secret.data.unwrap().get("release").unwrap().0.clone();
                //base64解密
                let release_data = decode(&release_data_base64);
                //gunzip 解密 gzip格式
                let mut decoder = Decoder::new(&release_data[..]).unwrap();
                let mut decoded_data = Vec::new();
                decoder.read_to_end(&mut decoded_data).unwrap();
                //gunzip解压后的字符串内容
                let release_str = String::from_utf8(decoded_data).unwrap();
                //println!("{}",&releaseStr)
                //分解处理解压后的release内容
                deal_with_release_str(
                    release_str.clone(),
                    secret_release_name.clone(),
                    secret_namespace.clone(),
                    savepath.clone(),
                );
            }
        }
    }
    Ok(())
}

//分解处理解压后的release内容
fn deal_with_release_str(
    /*解压后的内容*/ release_str: String,
    /*已安装chart名称*/ release_name: String,
    namespace: String,
    savepath: String,
) {
    //println!("{}", release_str);
    //按行分解出要处理的json字符串
    for line in release_str.lines() {
        if line != "" {
            let json: Value = serde_json::from_str(line).unwrap();
            let name = json["name"].as_str().unwrap();

            let status = json["info"]["status"].as_str().unwrap();
            if status == "deployed" && name == &release_name {
                //println!("{}", line);
                //状态是已部署的
                deal_with_release_json(json.clone(), release_name, namespace, savepath.clone());
                break;
            }
        }
    }
}

fn deal_with_release_json(
    /*解压后的内容*/ json: Value,
    /*已安装chart名称*/ _release_name: String,
    namespace: String,
    savepath: String,
) {
    let chart: Value = json["chart"].clone(); //chart数据
                                              //处理chart中各文件

    //包名
    let chart_name = chart["metadata"]["name"].as_str().unwrap();
    //版本
    let chart_version = chart["metadata"]["version"].as_str().unwrap();

    //先进行tar打包
    //let tar_file = File::create(format!("{}-{}.tar",&chart_name,&chart_version)).unwrap();
    let mut tar_header = Header::new_gnu();

    let mut tar_file_builder = Builder::new(Vec::new());

    //Chart.yaml
    let chart_yaml = serde_yaml::to_string(&chart["metadata"])
        .unwrap()
        .trim()
        .replace("---\n", "");
    //println!("Chart.yaml\n{}",&chart_yaml);
    //文件加进tar文件数据里
    tar_header.set_size(chart_yaml.as_bytes().len().try_into().unwrap());
    tar_header.set_cksum();
    tar_file_builder
        .append_data(
            &mut tar_header,
            format!("{}/Chart.yaml", &chart_name),
            chart_yaml.as_bytes(),
        )
        .unwrap();

    //values.yaml

    let values_yaml = serde_yaml::to_string(&chart["values"])
        .unwrap()
        .trim()
        .replace("---\n", "");

    //println!("values.yaml\n{}",&values_yaml);
    //文件加进tar文件数据里
    tar_header.set_size(values_yaml.as_bytes().len().try_into().unwrap());
    tar_header.set_cksum();
    tar_file_builder
        .append_data(
            &mut tar_header,
            format!("{}/values.yaml", &chart_name),
            values_yaml.as_bytes(),
        )
        .unwrap();

    for template in chart["templates"].as_array().unwrap() {
        //模板文件路径
        let template_name = template["name"].as_str().unwrap();
        //模板文件base64加密后
        let template_data = template["data"].as_str().unwrap();
        //模板文件base64解密后
        let template_data_raw = decode(template_data.as_bytes());

        //println!("{}\n{}",&template_name ,String::from_utf8(template_data_raw.clone()).unwrap());
        //文件加进tar文件数据里
        println!("{}", &template_name);
        tar_header.set_size(template_data_raw.len().try_into().unwrap());
        tar_header.set_cksum();
        tar_file_builder
            .append_data(
                &mut tar_header,
                format!("{}/{}", &chart_name, &template_name),
                &template_data_raw[..],
            )
            .unwrap();
    }

    //gzip打包
    if fs::read_dir(format!("{}/{}", &savepath, &namespace)).is_err() {
        // 子目录不存在,递归创建
        let _ = fs::create_dir_all(format!("{}/{}", &savepath, &namespace));
    }
    let tgz_file = File::create(format!(
        "{}/{}/{}-{}.tgz",
        &savepath, &namespace, &chart_name, &chart_version
    ))
    .unwrap();
    let mut encoder = Encoder::new(tgz_file).unwrap();
    encoder
        .write_all(&tar_file_builder.into_inner().unwrap())
        .unwrap();
    encoder.finish().into_result().unwrap();

    //用户自定义values输出
    let user_provided_values_yaml = serde_yaml::to_string(&json["config"])
        .unwrap()
        .trim()
        .replace("---\n", "");

    fs::write(
        format!(
            "{}/{}/{}-{}-user-provided-values.yaml",
            &savepath, &namespace, &chart_name, &chart_version
        ),
        user_provided_values_yaml.as_bytes(),
    )
    .unwrap();
}
